{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "# Implementation of Breadth First Search\n",
    "\n",
    "\n",
    "An alternative algorithm called Breath-First search provides us with the ability to return the same results as DFS but with the added guarantee to return the shortest-path first. This algorithm is a little more tricky to implement in a recursive manner instead using the queue data-structure, as such I will only being documenting the iterative approach. The actions performed per each explored vertex are the same as the depth-first implementation, however, replacing the stack with a queue will instead explore the breadth of a vertex depth before moving on. This behavior guarantees that the first path located is one of the shortest-paths present, based on number of edges being the cost factor.\n",
    "\n",
    "We'll assume our Graph is in the form:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "graph = {'A': set(['B', 'C']),\n",
    "         'B': set(['A', 'D', 'E']),\n",
    "         'C': set(['A', 'F']),\n",
    "         'D': set(['B']),\n",
    "         'E': set(['B', 'F']),\n",
    "         'F': set(['C', 'E'])}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Connected Component\n",
    "Similar to the iterative DFS implementation the only alteration required is to remove the next item from the beginning of the list structure instead of the stacks last."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'A', 'B', 'C', 'D', 'E', 'F'}"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def bfs(graph, start):\n",
    "    visited, queue = set(), [start]\n",
    "    while queue:\n",
    "        vertex = queue.pop(0)\n",
    "        if vertex not in visited:\n",
    "            visited.add(vertex)\n",
    "            queue.extend(graph[vertex] - visited)\n",
    "    return visited\n",
    "\n",
    "bfs(graph, 'A')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Paths\n",
    "This implementation can again be altered slightly to instead return all possible paths between two vertices, the first of which being one of the shortest such path."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[['A', 'C', 'F'], ['A', 'B', 'E', 'F']]"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def bfs_paths(graph, start, goal):\n",
    "    queue = [(start, [start])]\n",
    "    while queue:\n",
    "        (vertex, path) = queue.pop(0)\n",
    "        for next in graph[vertex] - set(path):\n",
    "            if next == goal:\n",
    "                yield path + [next]\n",
    "            else:\n",
    "                queue.append((next, path + [next]))\n",
    "\n",
    "list(bfs_paths(graph, 'A', 'F'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Knowing that the shortest path will be returned first from the BFS path generator method we can create a useful method which simply returns the shortest path found or ‘None’ if no path exists. As we are using a generator this in theory should provide similar performance results as just breaking out and returning the first matching path in the BFS implementation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['A', 'C', 'F']"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def shortest_path(graph, start, goal):\n",
    "    try:\n",
    "        return next(bfs_paths(graph, start, goal))\n",
    "    except StopIteration:\n",
    "        return None\n",
    "\n",
    "shortest_path(graph, 'A', 'F')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Resources\n",
    "* [Depth-and Breadth-First Search](http://jeremykun.com/2013/01/22/depth-and-breadth-first-search/)\n",
    "* [Connected component](https://en.wikipedia.org/wiki/Connected_component_(graph_theory))\n",
    "* [Adjacency matrix](https://en.wikipedia.org/wiki/Adjacency_matrix)\n",
    "* [Adjacency list](https://en.wikipedia.org/wiki/Adjacency_list)\n",
    "* [Python Gotcha: Default arguments and mutable data structures](https://developmentality.wordpress.com/2010/08/23/python-gotcha-default-arguments/)\n",
    "* [Generators](https://wiki.python.org/moin/Generators)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
